package pw.goyd.game.server;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.timeout.IdleStateHandler;
import org.jboss.netty.util.HashedWheelTimer;
import pw.goyd.db.RedisUtil;
import pw.goyd.game.api.LuckyPoolImp;
import pw.goyd.game.api.RunningTask;
import pw.goyd.game.constant.setting.GameSetting;
import pw.goyd.game.constant.setting.ServerSetting;
import pw.goyd.game.constant.setting.StateCode;
import pw.goyd.game.handler.CommandReflect;
import pw.goyd.protocol.codc.RequestDecoder;
import pw.goyd.protocol.codc.ResponseEncoder;
import pw.goyd.subscribe.BinaryRedisPubSubListener;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;

import java.io.InputStream;
import java.net.InetSocketAddress;
import java.util.Timer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server implements ServerSetting, StateCode, GameSetting {
    public static SqlSessionFactory sessionFactory = null;
    // 服务端时间 用来验证延迟
    private static int serverTimeSec = 1000000;

    public static void main(String[] args) throws Exception {
        //Basic Info
        Log.getLogger().info("斗牛Server  Version:" + S_VERSION);

        // initMyBatis
        Log.getLogger().info("载入Mybatis相关..");
        initMyBatis();
        //initRedis
        Log.getLogger().info("载入订阅服务..");
        RedisUtil.init();
        //initReflect
        Log.getLogger().info("初始化指令映射表.");
        CommandReflect.init();
        // run server timer
        Log.getLogger().info("启动定时任务.");
        serverTimer();
        //LuckyAward
        Log.getLogger().info("初始化转盘数据..");
        LuckyPoolImp.init();
        // bind Server
        runServer();
        // gui msg
        // ControlWindow controlWindow = new ControlWindow();

    }

    private static void initMyBatis() {
        // 使用类加载器加载mybatis的配置文件（它也加载关联的映射文件）
        InputStream is = Server.class.getResourceAsStream(MYBATIS_RES);
        sessionFactory = new SqlSessionFactoryBuilder().build(is);  //  构建sqlSession的工厂
    }

    private static void runServer() {
        // Build Server Class
        ServerBootstrap bootstrap = new ServerBootstrap();

        // Build Thread Pool
        ExecutorService boss = Executors.newCachedThreadPool();
        ExecutorService worker = Executors.newCachedThreadPool();

        // Set NIO Socket Factory
        bootstrap.setFactory(new NioServerSocketChannelFactory(boss, worker));

        // IdelState Timer
        final HashedWheelTimer hashedWheelTimer = new HashedWheelTimer();

        // Set NIO Channel Factory
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            @Override
            public ChannelPipeline getPipeline() throws Exception {

                ChannelPipeline pipeline = Channels.pipeline();

                // IdleStateHanler
                pipeline.addLast("idle", new
                        IdleStateHandler(hashedWheelTimer, 60, 60, 120));

                //Receive Decoder
                pipeline.addLast("decoder", new RequestDecoder());
                // Handler
                pipeline.addLast("Router", new Router());
                // Send Encoder
                pipeline.addLast("encoder", new ResponseEncoder());

                return pipeline;
            }
        });


        // Bind Port
        bootstrap.bind(new InetSocketAddress(S_PORT));
        Log.getLogger().info("WorldChannel run on [" + S_PORT + "]");

        //  Msg订阅
        Log.getLogger().info("初始化消息控制台订阅");
        // 新建一个Listener 为redisclient监听订阅
        BinaryRedisPubSubListener listener = new BinaryRedisPubSubListener();
        Jedis client = RedisUtil.getJedis();        //  从池中获取
        if (client == null) {
            Log.getLogger().error("Jedis Pool is empty");
            Thread.currentThread().stop();
        }

        // 阻塞监听线程
        Thread msgTerTh;
        msgTerTh = new Thread(() -> {
            try {
                Log.getLogger().info("Msg Terminal is Listening");
                client.psubscribe(listener,
                        ("MsgTerminal").getBytes());
            } catch (JedisException e) {
                Log.getLogger().error("JedisExp", e);
            } catch (ClassCastException e) {
                Log.getLogger().error(
                        "B cannot be cast to java.util.List", e);
            } catch (NullPointerException e) {
                Log.getLogger().error("redis链接失败 请检查启动", e);
            }
        });
        msgTerTh.start();
        // 阻塞监听线程
        Thread annTerTh;
        annTerTh = new Thread(() -> {
            try {
                Log.getLogger().info("Announce is Listening");
                client.psubscribe(listener,
                        ("Announce").getBytes());
            } catch (JedisException e) {
                Log.getLogger().error("JedisExp", e);
            } catch (ClassCastException e) {
                Log.getLogger().error(
                        "B cannot be cast to java.util.List", e);
            } catch (NullPointerException e) {
                Log.getLogger().error("redis链接失败 请检查启动", e);
            }
        });
        annTerTh.start();


    }

    /**
     * 定时任务
     * 利用单线程来检查房间列的自动推进
     */
    private static void serverTimer() {
        Timer timer = new Timer();


        timer.schedule(new RunningTask(), 0, 1000);     // 计时器和任务处理
    }

    public static int getServerTime() {
        return serverTimeSec;
    }

    public static void addServerTime() {
        serverTimeSec++;
    }

}
